#include <lversion.h>
#if L_VERSION_MODULE_SIMULATOR == 111225

#include "_lsimulator_111225.h"

#include <lutility.h>

#include <gl/openglut.h>

using namespace boost;
using namespace llib;
using namespace llib::geom;

llib::Navigator::Navigator( shared_ptr<Simulator> simulator ) : 
    pOriSim         ( simulator ),
    _eye            ( Vec3f(0.f,0.f,10.f) ),
    _direction      ( Vec3f(0.f,0.f,-1.f) ),
    _eyeCenterDis   ( 1.f ),
    _moveStep       ( 0.2f ),
    _rotationRatio  ( 0.005f ),
    _yDirectionMax  ( 0.8f )
{
    BOOST_ASSERT(simulator.use_count() != 0);
}

llib::Navigator::Navigator( Simulator* simulator ) :
    pOriSim         ( shared_ptr<Simulator>(simulator) ),
    _eye            ( Vec3f(0.f,0.f,10.f) ),
    _direction      ( Vec3f(0.f,0.f,-1.f) ),
    _eyeCenterDis   ( 1.f ),
    _moveStep       ( 0.2f ),
    _rotationRatio  ( 0.005f ),
    _yDirectionMax  ( 0.8f )
{
    BOOST_ASSERT(simulator != NULL);
}

llib::Boolean llib::Navigator::Render()
{
    Vec3f center = _eye + _direction * _eyeCenterDis;
    gluLookAt( _eye.x, _eye.y, _eye.z, center.x, center.y, center.z, 0.f, 1.f, 0.f );

    return pOriSim->Render();
}

llib::Boolean llib::Navigator::OnKeyDown( Uint8 key, Int32 x, Int32 y, Uint8 modifiers )
{
    if(modifiers == 0) {
        switch (key) {
            case 'w':
                _eye += _moveStep * _direction;
                break;
            case 's':
                _eye -= _moveStep * _direction;
                break;
            case 'a': {
                Vec3f leftDirection;
                leftDirection.x = _direction.z;
                leftDirection.y = 0.f;
                leftDirection.z = -_direction.x;
                leftDirection.Normalize();
                _eye += _moveStep * leftDirection;
                break; }
            case 'd': {
                Vec3f rightDirection;
                rightDirection.x = -_direction.z;
                rightDirection.y = 0.f;
                rightDirection.z = _direction.x;
                rightDirection.Normalize();
                _eye += _moveStep * rightDirection;
                break; }
            default:
                break;
        }
    }

    return pOriSim->OnKeyDown(key, x, y, modifiers);
}

llib::Boolean llib::Navigator::OnSpecialKeyDown( Uint8 key, Int32 x, Int32 y, Uint8 modifiers )
{
    if(modifiers == 0) {
        switch (key) {
            case GLUT_KEY_UP:
                _eye += _moveStep * _direction;
                break;
            case GLUT_KEY_DOWN:
                _eye -= _moveStep * _direction;
                break;
            case GLUT_KEY_LEFT: {
                Vec3f leftDirection;
                leftDirection.x = _direction.z;
                leftDirection.y = 0.f;
                leftDirection.z = -_direction.x;
                leftDirection.Normalize();
                _eye += _moveStep * leftDirection;
                break; }
            case GLUT_KEY_RIGHT: {
                Vec3f rightDirection;
                rightDirection.x = -_direction.z;
                rightDirection.y = 0.f;
                rightDirection.z = _direction.x;
                rightDirection.Normalize();
                _eye += _moveStep * rightDirection;
                break; }
            default:
                break;
        }
    }

    return pOriSim->OnSpecialKeyDown(key, x, y, modifiers);
}

llib::Boolean llib::Navigator::OnMouseButtonDown( Uint8 button, Int32 x, Int32 y )
{
    if(button == llib::util::MouseButtonRight) {
        _mouseDragStart.x = x;
        _mouseDragStart.y = y;
        _lastDirection    = _direction;
    }

    return pOriSim->OnMouseButtonDown(button, x, y);
}

llib::Boolean llib::Navigator::OnMouseMove( Uint8 button, Int32 x, Int32 y )
{
    // [2011-12-1]: the mouse move navigation still seems not so smooth!
    if((button & llib::util::MouseButtonRight) != 0) {
        Int32 hDis = x - _mouseDragStart.x;
        Int32 vDis = y - _mouseDragStart.y;
        Float hAngle = -hDis * _rotationRatio;
        Float vAngle = -vDis * _rotationRatio;
        Float cos1 = cosf(hAngle), sin1 = sinf(hAngle);
        Float cos2 = cosf(vAngle), sin2 = sinf(vAngle);
        if( (_direction.y > _yDirectionMax && vAngle > 0) ||
            (_direction.y < -_yDirectionMax && vAngle < 0) ) {
            // disable vertical angle moving when vAngle out off range
            //_direction.x = -(_lastDirection.x * cos1 - _lastDirection.z * sin1);
            //_direction.z = _lastDirection.x * sin1 + _lastDirection.z * cos1;
        } else {
            _direction.x = _lastDirection.x * cos1 + _lastDirection.y * sin1 * sin2 + _lastDirection.z * sin1 * cos2;
            _direction.y = _lastDirection.y * cos2 - _lastDirection.z * sin2;
            _direction.z = _lastDirection.x * (-sin1) + _lastDirection.y * cos1 * sin2 + _lastDirection.z * cos1 * cos2;
        }
    }

    return pOriSim->OnMouseMove(button, x, y);
}

#endif
